//
//  NetworkConnectionMonitor.m
//  Words2
//
//  Created by Mathieu Tozer on 1/09/06.
//  Copyright 2006 __MyCompanyName__. All rights reserved.
//

#import "NetworkConnectionMonitor.h"

#import <assert.h>
#import <errno.h>

#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
//#include <SystemConfiguration/SCNetworkReachability.h>

@implementation NetworkConnectionMonitor

-(void)connectAndMonitorConnectionTo:(NSString *)server{
	const char *serverString = [server cString];
		const char * argv[] = {
			"serverReacher", serverString
		};
		connectTo(self, 2, argv);
}

-(void)stopMonitoring{
	// Clean up.
    
    // The current implementation never gets to this point in the success 
    // case because the way we terminate the program is with ^C (SIGINT), 
    // which just quits the program.  Alas, there's no easy way to 
    // integrate signal handling into a CFRunLoop <rdar://problem/3635076>.  
    // However, I wanted to leave the termination code around, just to show 
    // how to do it properly.
    
//    if (targets != NULL) {
//        int i;
//        
//        for (i = 1; i < argIndex; i++) {
//            assert(targets[i] != NULL);
//            CleanupTarget(targets[i]);
//        }
//        free(targets);
//    }
//	
//    if (err == 0) {
//        return EXIT_SUCCESS;
//    } else {
//        if (err != ECANCELED) {
//            fprintf(stderr, "Failed with error %d\n.", err);
//        }
//        return EXIT_FAILURE;
//    }
}

- (bool)isAvailable {
    return isAvailable;
}

- (void)setIsAvailable:(bool)value {
    if (isAvailable != value) {
        isAvailable = value;
    }
}





enum {
    kCallbackParamsMagic = 0x66642666
};

struct CallbackParams {
    int                             magic;      // kCallbackParamsMagic
    Boolean                         forcePrintStatus;
    SCNetworkConnectionStatus       lastMajorStatus;
    SCNetworkConnectionPPPStatus    lastMinorStatus;
};
typedef struct CallbackParams CallbackParams;
static void CleanupTarget(SCNetworkReachabilityRef thisTarget)
    // Disposes of thisTarget.
{
    assert(thisTarget != NULL);
    
    // Ignore the result from SCNetworkReachabilityUnscheduleFromRunLoop. 
    // It basically tells us whether thisTarget was successfully removed 
    // from the runloop.  It will be false if we never added thisTarget 
    // to the runloop, which is a definite possibility under error 
    // conditions.
    
    (void) SCNetworkReachabilityUnscheduleFromRunLoop(
        thisTarget,
        CFRunLoopGetCurrent(),
        kCFRunLoopDefaultMode
    );
    CFRelease(thisTarget);
}

static void PrintHeader(void)
    // Prints an explanation of the flag coding.
{
    fprintf(stdout, "t = kSCNetworkFlagsTransientConnection\n");
    fprintf(stdout, "r = kSCNetworkFlagsReachable\n");
    fprintf(stdout, "c = kSCNetworkFlagsConnectionRequired\n");
    fprintf(stdout, "C = kSCNetworkFlagsConnectionAutomatic\n");
    fprintf(stdout, "i = kSCNetworkFlagsInterventionRequired\n");
    fprintf(stdout, "l = kSCNetworkFlagsIsLocalAddress\n");
    fprintf(stdout, "d = kSCNetworkFlagsIsDirect\n");
    fprintf(stdout, "\n");
}

static void PrintReachabilityFlags(
    const char *                hostname, 
    SCNetworkConnectionFlags    flags, 
    const char *                comment
)
    // Prints a line that records a reachability transition. 
    // This includes the current time, the new state of the 
    // reachability flags (from the flags parameter), and the 
    // name of the host (from the hostname parameter).
{
    time_t      now;
    struct tm   nowLocal;
    char        nowLocalStr[30];
    
    assert(hostname != NULL);
    
    if (comment == NULL) {
        comment = "";
    }
    
    (void) time(&now);
    (void) localtime_r(&now, &nowLocal);
    (void) strftime(nowLocalStr, sizeof(nowLocalStr), "%X", &nowLocal);
	
	//set the reachable bool for the words application's dictionary objects to check against.
	


	

    fprintf(stdout, "%s %c%c%c%c%c%c%c %s%s\n",
        nowLocalStr,
        (flags & kSCNetworkFlagsTransientConnection)  ? 't' : '-',
        (flags & kSCNetworkFlagsReachable)            ? 'r' : '-',
        (flags & kSCNetworkFlagsConnectionRequired)   ? 'c' : '-',
        (flags & kSCNetworkFlagsConnectionAutomatic)  ? 'C' : '-',
        (flags & kSCNetworkFlagsInterventionRequired) ? 'i' : '-',
        (flags & kSCNetworkFlagsIsLocalAddress)       ? 'l' : '-',
        (flags & kSCNetworkFlagsIsDirect)             ? 'd' : '-',
        hostname,
        comment
    );
}

static void MyReachabilityCallback(
    SCNetworkReachabilityRef	target,
    SCNetworkConnectionFlags	flags,
    void *                      info
)

{
    assert(target != NULL);
    assert(info   != NULL);
    
	id self = (id)info;
	
	if(flags & kSCNetworkFlagsTransientConnection){
		[self setIsAvailable:NO];
	}  
	else if(flags & kSCNetworkFlagsReachable){
		[self setIsAvailable:YES];
		NSLog(@"Set network connection isAvailble");
	}
	else if(flags & kSCNetworkFlagsConnectionRequired){
		[self setIsAvailable:NO];
	}
	else{
		[self setIsAvailable:NO];
		NSLog(@"Cannot connect to network! (tried all Flags)");
	}
 }

int connectTo (id self, int argc, const char * argv[])
    // This program treats each of its arguments as a network host 
    // name.  It monitors the reachability of each host, printing 
    // out a record of each change of reachability.
{
    int                             err;
    int                             argIndex;
    SCNetworkReachabilityRef *      targets;
    Boolean                         ok;

    targets = NULL;

    // If we're run with no arguments, just print the usage.
    
    err = 0;
    if (argc == 1) {
        const char *programName;
        
        programName = strrchr(argv[0], '/');
        if (programName == NULL) {
            programName = argv[0];
        } else {
            programName += 1;
        }
        fprintf(stderr, "Usage: %s hostname...\n", programName);
        err = ECANCELED;
    }

    // We allocate an array to hold all of the SCNetworkReachabilityRefs
    // that we create.  The 0'th slot of this array is never used, but 
    // wasting that trivial amount of memory makes the code easier.
    
    if (err == 0) {
        targets = malloc(argc * sizeof(*targets));
        if (targets == NULL) {
            err = ENOMEM;
        }
    }
    
    // Create a SCNetworkReachabilityRef for each argument.
    
    if (err == 0) {
     //   PrintHeader();
     //   fprintf(stdout, "Hit ^C to exit.\n");
        fflush(stdout);
        
        for (argIndex = 1; argIndex < argc; argIndex++) {
            SCNetworkReachabilityRef        thisTarget;
            SCNetworkReachabilityContext    thisContext;
            
            thisContext.version         = 0;
            thisContext.info            = (void *) self;
            thisContext.retain          = NULL;
            thisContext.release         = NULL;
            thisContext.copyDescription = NULL;
            
            // Create the target with the name taken from the command 
            // line arguments.
            
            thisTarget = SCNetworkReachabilityCreateWithName(
                NULL, 
                argv[argIndex]
            );
            if (thisTarget == NULL) {
                err = SCError();
            }
      
            // Set our callback and install on the runloop.
            
            if (err == 0) {
                ok = SCNetworkReachabilitySetCallback(
                        thisTarget, 
                        MyReachabilityCallback,
                        &thisContext
                );
                if ( ! ok ) {
                    err = SCError();
                }
            }
            if (err == 0) {
                ok = SCNetworkReachabilityScheduleWithRunLoop(
                    thisTarget, 
                    CFRunLoopGetCurrent(), 
                    kCFRunLoopDefaultMode
                );
                if ( ! ok ) {
                    err = SCError();
                }
            }


            if (err == 0) {
                SCNetworkConnectionFlags flags;
                
                ok = SCNetworkReachabilityGetFlags(thisTarget, &flags);
                if ( ok ) {
                    PrintReachabilityFlags(
                        argv[argIndex], 
                        flags, 
                        " (from main)"
                    );
					if(flags & kSCNetworkFlagsTransientConnection){
						[self setIsAvailable:NO];
					}  
					else if(flags & kSCNetworkFlagsReachable){
						[self setIsAvailable:YES];
						NSLog(@"Set network connection isAvailble");
					}
					else if(flags & kSCNetworkFlagsConnectionRequired){
						[self setIsAvailable:NO];
					}
					else{
						[self setIsAvailable:NO];
						NSLog(@"Cannot connect to network! (tried all Flags)");
					}
					
                } else {
                    err = SCError();
                }
            }
			
            
            // Record the reference in the targets array, or clean it 
            // up and bail.
            
            if (err == 0) {
                targets[argIndex] = thisTarget;
            } else {
                if (thisTarget != NULL) {
                    CleanupTarget(thisTarget);
                }
                break;
            }
        }
    }
    
//moved cleanup code
	return 1;
}


@end
